package http;

import utils.IOUtils;
import utils.TextUtils;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;

/**
 * Created by taoweisong on 16/3/15.
 */

public class HttpUrlConnection {
    private static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%";
    private static final String CHARSET = "UTF-8";
    /**
     * 建立连接的超时时间
     */
    private static final int connectTimeout = 6 * 1000;
    /**
     * 建立到资源的连接后从 input 流读入时的超时时间
     */
    private static final int readTimeout = 10 * 1000;

    private static HttpUrlConnection instance;

    public static HttpUrlConnection getInstance() {
        if (instance == null) {
            synchronized (HttpUrlConnection.class) {
                if (instance == null) {
                    instance = new HttpUrlConnection();
                }
            }
        }
        return instance;
    }

    CookieManager cookieManager;

    private HttpUrlConnection() {
        cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
        HttpsURLConnection.setFollowRedirects(true);
        CookieHandler.setDefault(cookieManager);
    }

    private HttpURLConnection createConnection(String url) throws IOException {
        HttpURLConnection conn = (HttpURLConnection) new URL(url)
                .openConnection();
        conn.setConnectTimeout(connectTimeout);
        conn.setReadTimeout(readTimeout);
        return conn;
    }

    private InputStream getInputStream(String url, HashMap<String, String> hashMap) {
        InputStream is = null;
        HttpURLConnection conn = null;
        String cookies = "";
        try {
            conn = createConnection(url);
            conn.setRequestMethod("GET");
            conn.setInstanceFollowRedirects(true);

            if (hashMap != null) {
                Iterator it = hashMap.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    conn.setRequestProperty(key, hashMap.get(key));
                }
            }

            is = conn.getInputStream();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return is;
    }

    private String getCookie(String url) {
        List<HttpCookie> cookieList = cookieManager.getCookieStore().get(URI.create(url));
        if (cookieList == null || cookieList.size() == 0) return null;
        StringBuilder sb = new StringBuilder();
        sb.append(cookieList.get(0).toString()).append(";");
        for (int i = 1; i < cookieList.size() - 1; i++) {
            sb.append(cookieList.get(0).toString()).append(";");
        }
        return sb.toString();
    }

    public String getString(String url, HashMap<String, String> hashMap) {
        String result = null;
        InputStream is = null;
        BufferedReader br = null;
        try {

            is = getInputStream(url, hashMap);
            if (is == null) return null;
            br = new BufferedReader(new InputStreamReader(is, CHARSET));
            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            result = sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.silentCloseCloseable(br);
            IOUtils.silentCloseCloseable(is);
        }

        return result;
    }

    public String postString(String url, String params, HashMap<String, String> hashMap) {
        String result = null;
        OutputStream os = null;
        InputStream is = null;
        BufferedReader br = null;
        Boolean isGzip = false;
        try {
            HttpURLConnection conn = createConnection(url);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);// POST方式不能缓存数据
            conn.setRequestProperty("Content-Type", "application/json; CHARSET=" + CHARSET);

            if (hashMap != null) {
                Iterator it = hashMap.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    conn.setRequestProperty(key, hashMap.get(key));
                    if (key.equalsIgnoreCase("Content-Encoding") && hashMap.get(key).equalsIgnoreCase("gzip"))
                        isGzip = true;
                }
            }

            if (params != null) {
                if (isGzip) {
                    os = conn.getOutputStream();
                    GZIPOutputStream gzipos = new GZIPOutputStream(os);
                    gzipos.write(params.getBytes(CHARSET));
                    gzipos.flush();
                    gzipos.close();
                } else {
                    os = conn.getOutputStream();
                    DataOutputStream dos = new DataOutputStream(os);
                    dos.write(params.getBytes(CHARSET));
                    dos.flush();
                    dos.close();
                }
            }

            if (conn.getResponseCode() == 200) {
                is = conn.getInputStream();
                if (is == null) return null;
                br = new BufferedReader(new InputStreamReader(is, CHARSET));
                String line = null;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                result = sb.toString();
            }

            if (conn.getResponseCode() != 200) {
                result = "";
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.silentCloseCloseable(os);
            IOUtils.silentCloseCloseable(br);
            IOUtils.silentCloseCloseable(is);
        }

        return result;
    }

    /**
     * 模拟GET请求
     *
     * @param url
     * @param hashMap
     * @param charsetName
     * @return
     */
    public String simulateGetString(String url, HashMap<String, String> hashMap, String charsetName) {
        String result = null;
        InputStream is = null;
        BufferedReader br = null;
        try {
            is = getInputStream(url, hashMap);
            if (is == null) return null;
            br = new BufferedReader(new InputStreamReader(is, charsetName == "" ? CHARSET : charsetName));
            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            result = sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.silentCloseCloseable(br);
            IOUtils.silentCloseCloseable(is);
        }

        return result;
    }

    /**
     * 模拟POST请求
     *
     * @param url
     * @param params
     * @param hashMap
     * @param charsetName
     * @param responseType 0:text 1:gzip
     * @return
     */
    public String simulatePostString(String url, String params, HashMap<String, String> hashMap, String charsetName, int responseType, int failCode) {
        String result = null;
        OutputStream os = null;
        InputStream is = null;
        BufferedReader br = null;
        try {
            HttpURLConnection conn = createConnection(url);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);// POST方式不能缓存数据
            //当failCode在300~400之间时，设置为false，不然response code会返回200，而不是3xx
            if (failCode >= 300 && failCode < 400) {
                conn.setInstanceFollowRedirects(false);
            }

            if (hashMap != null) {
                Iterator it = hashMap.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    conn.setRequestProperty(key, hashMap.get(key));
                }
            }

            if (params != null) {
                os = conn.getOutputStream();
                DataOutputStream dos = new DataOutputStream(os);
                dos.write(params.getBytes(CHARSET));
                dos.flush();
                dos.close();
            }

            if (conn.getResponseCode() == 200) {
                is = conn.getInputStream();

                if (is == null) return null;
                charsetName = "UTF-8";

                if (responseType == 0)
                    br = new BufferedReader(new InputStreamReader(is, charsetName));
                else if (responseType == 1)
                    br = new BufferedReader(new InputStreamReader(new GZIPInputStream(is), charsetName));

                if (br == null) return null;
                String line = null;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                result = sb.toString();
            }

            if (conn.getResponseCode() >= failCode) {
                return result;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.silentCloseCloseable(os);
            IOUtils.silentCloseCloseable(br);
            IOUtils.silentCloseCloseable(is);
        }

        return result;
    }

    /**
     * 模拟POST请求(返回字节数组)
     *
     * @param url
     * @param params
     * @param hashMap
     * @return
     */
    public String simulatePostString(String url, String params, HashMap<String, String> hashMap, String charsetName, int failCode) {
        OutputStream os = null;
        InputStream is = null;
        BufferedReader br = null;
        String result = null;
        Map<String, List<String>> resHeaders = null;
        try {
            HttpURLConnection conn = createConnection(url);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);// POST方式不能缓存数据
            if (failCode >= 300 && failCode < 400) {
                conn.setInstanceFollowRedirects(false);
            }

            if (hashMap != null) {
                Iterator it = hashMap.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    conn.setRequestProperty(key, hashMap.get(key));
                }
            }

            if (params != null) {
                os = conn.getOutputStream();
                DataOutputStream dos = new DataOutputStream(os);
                dos.write(params.getBytes(CHARSET));
                dos.flush();
                dos.close();
            }

            int statusCode = conn.getResponseCode();
            if (statusCode == 200) {
                is = conn.getInputStream();

                if (is == null) return null;
                resHeaders = conn.getHeaderFields();
                if (resHeaders.containsKey("content-encoding")) {
                    boolean hasGzip = false;
                    for (String contentEncoding : resHeaders.get("content-encoding")) {
                        if ("gzip".equalsIgnoreCase(contentEncoding)) {
                            hasGzip = true;
                            break;
                        }
                    }
                    if (hasGzip) {
                        br = new BufferedReader(new InputStreamReader(new GZIPInputStream(is), charsetName));
                    } else {
                        br = new BufferedReader(new InputStreamReader(is, CHARSET));
                    }
                } else {
                    br = new BufferedReader(new InputStreamReader(is, CHARSET));
                }

                String line = null;
                StringBuilder sb = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                result = sb.toString();
            }

            if (statusCode >= failCode) {
                return result;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.silentCloseCloseable(os);
            IOUtils.silentCloseCloseable(br);
            IOUtils.silentCloseCloseable(is);
        }

        return result;
    }


    public byte[] post(String url, String params, HashMap<String, String> hashMap, String charsetName, int failCode) {
        InputStream is = null;
        ByteArrayOutputStream bos = null;
        OutputStream os = null;
        byte[] buffer = null;
        Map<String, List<String>> resHeaders = null;
        try {
            String cookies = "";
            HttpURLConnection conn = createConnection(url);

            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);// POST方式不能缓存数据
            if (failCode >= 300 && failCode < 400) {
                conn.setInstanceFollowRedirects(false);
            }

            if (hashMap != null) {
                Iterator it = hashMap.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    conn.setRequestProperty(key, hashMap.get(key));
                }
            }

            if (params != null) {
                os = conn.getOutputStream();
                DataOutputStream dos = new DataOutputStream(os);
                dos.write(params.getBytes(CHARSET));
                dos.flush();
                dos.close();
            }

            int statusCode = conn.getResponseCode();

            if (statusCode == 200 || statusCode == 201) {
                is = conn.getInputStream();
                if (is == null) return null;
                resHeaders = conn.getHeaderFields();
                int bytesRead = -1;
                buffer = new byte[1024 * 8];
                bos = new ByteArrayOutputStream();
                if (resHeaders.containsKey("content-encoding")) {
                    boolean hasGzip = false;
                    for (String contentEncoding : resHeaders.get("content-encoding")) {
                        if ("gzip".equalsIgnoreCase(contentEncoding)) {
                            hasGzip = true;
                            break;
                        }
                    }
                    if (hasGzip) {
                        is = new GZIPInputStream(is);
                    }
                }

                if (is != null) {
                    while ((bytesRead = is.read(buffer)) != -1) {
                        bos.write(buffer, 0, bytesRead);
                    }
                }
                return bos.toByteArray();
            }

            if (statusCode >= failCode) {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.silentCloseCloseable(bos);
            IOUtils.silentCloseCloseable(is);
        }

        return null;
    }

    public static void copy(InputStream in, OutputStream out, int bufferSize)
            throws IOException {
        // Read bytes and write to destination until eof
        byte[] buf = new byte[bufferSize];
        int len = 0;
        while ((len = in.read(buf)) >= 0) {
            out.write(buf, 0, len);
        }
    }
}